const express = require('express');
const http = require('http');
const WebSocket = require('ws');
const cors = require('cors');
const compression = require('compression');
const NetworkDiscovery = require('./modules/NetworkDiscovery');
const MessageHandler = require('./modules/MessageHandler');
const FileTransferHandler = require('./modules/FileTransferHandler');
const DatabaseManager = require('./modules/DatabaseManager');
const SignalingServer = require('./modules/SignalingServer');
const { getLogger } = require('./utils/Logger');

// Validate environment variables
const PORT = parseInt(process.env.PORT || '3000');
const WS_PORT = parseInt(process.env.WS_PORT || '3001');

if (PORT < 1 || PORT > 65535 || WS_PORT < 1 || WS_PORT > 65535) {
  throw new Error('Invalid port number. Ports must be between 1 and 65535');
}

if (PORT === WS_PORT) {
  throw new Error('HTTP and WebSocket ports must be different');
}

class LANChatServer {
  constructor() {
    this.app = express();
    this.server = http.createServer(this.app);
    this.wss = new WebSocket.Server({ port: WS_PORT });
    this.logger = getLogger({
      logLevel: process.env.LOG_LEVEL || 'info',
      enableConsole: true,
      enableFile: true
    });
    this.db = new DatabaseManager();
    this.networkDiscovery = new NetworkDiscovery();
    this.messageHandler = new MessageHandler(this.db);
    this.fileTransferHandler = new FileTransferHandler(this.db);
    this.signalingServer = new SignalingServer(this.wss);

    this.clients = new Map();
    this.setupMiddleware();
    this.setupRoutes();
    this.setupWebSocket();

    // Rotate logs daily
    this.logRotationInterval = setInterval(() => {
      this.logger.rotateLogs(7);
    }, 24 * 60 * 60 * 1000);
  }

  setupMiddleware() {
    this.app.use(cors({
      origin: process.env.ALLOWED_ORIGINS?.split(',') || '*',
      credentials: true
    }));
    this.app.use(compression());
    // 移除 JSON 和 URL 编码限制，让 multer 处理文件大小
    this.app.use(express.json());
    this.app.use(express.urlencoded({ extended: true }));
  }

  setupRoutes() {
    // Health check
    this.app.get('/health', (req, res) => {
      res.json({ status: 'ok', timestamp: Date.now() });
    });

    // Get online peers
    this.app.get('/api/peers', (req, res) => {
      const peers = Array.from(this.clients.values()).map(client => ({
        id: client.id,
        name: client.name,
        ipAddress: client.ipAddress,
        lastSeen: client.lastSeen
      }));
      res.json({ peers });
    });

    // File upload endpoint
    this.app.post('/api/upload', this.fileTransferHandler.handleUpload.bind(this.fileTransferHandler));

    // File download endpoint
    this.app.get('/api/download/:fileId', this.fileTransferHandler.handleDownload.bind(this.fileTransferHandler));

    // Message history - get conversation between current client and peer
    this.app.get('/api/messages/:peerId', (req, res) => {
      try {
        const limit = parseInt(req.query.limit) || 100;
        const offset = parseInt(req.query.offset) || 0;
        const messages = this.db.getMessageHistory(req.params.peerId, limit, offset);
        const totalCount = this.db.getMessageCount(req.params.peerId);

        res.json({
          messages,
          pagination: {
            limit,
            offset,
            total: totalCount,
            hasMore: offset + messages.length < totalCount
          }
        });
      } catch (error) {
        console.error('Error fetching message history:', error);
        res.status(500).json({ error: 'Failed to fetch message history' });
      }
    });
  }

  setupWebSocket() {
    this.wss.on('connection', (ws, req) => {
      const clientIp = req.socket.remoteAddress;
      console.log(`New client connected from ${clientIp}`);

      ws.on('message', (data) => {
        // Limit message size to 1MB
        if (data.length > 1024 * 1024) {
          ws.send(JSON.stringify({
            type: 'error',
            message: 'Message too large (max 1MB)'
          }));
          return;
        }

        try {
          const message = JSON.parse(data);

          // Validate message type
          const validTypes = ['register', 'message', 'file-meta', 'webrtc-signal', 'heartbeat'];
          if (!message.type || !validTypes.includes(message.type)) {
            throw new Error('Invalid message type');
          }

          this.handleWebSocketMessage(ws, message, clientIp);
        } catch (error) {
          console.error('Error parsing message:', error);
          ws.send(JSON.stringify({ type: 'error', message: 'Invalid message format' }));
        }
      });

      ws.on('close', () => {
        this.handleClientDisconnect(ws);
      });

      ws.on('error', (error) => {
        console.error('WebSocket error:', error);
      });
    });
  }

  handleWebSocketMessage(ws, message, clientIp) {
    switch (message.type) {
      case 'register':
        this.handleClientRegister(ws, message, clientIp);
        break;
      case 'message':
        this.messageHandler.handleMessage(ws, message, this.clients);
        break;
      case 'file-meta':
        this.fileTransferHandler.handleFileMeta(ws, message, this.clients);
        break;
      case 'webrtc-signal':
        // Handle different signal types
        const { signalType } = message;
        if (signalType === 'call-request') {
          this.signalingServer.handleCallRequest(ws, message, this.clients);
        } else if (signalType === 'call-accepted' || signalType === 'call-rejected') {
          this.signalingServer.handleCallResponse(ws, message, this.clients);
        } else {
          this.signalingServer.handleSignal(ws, message, this.clients);
        }
        break;
      case 'heartbeat':
        this.handleHeartbeat(ws, message);
        break;
      default:
        ws.send(JSON.stringify({ type: 'error', message: 'Unknown message type' }));
    }
  }

  handleClientRegister(ws, message, clientIp) {
    const clientId = message.clientId;
    const clientInfo = {
      id: clientId,
      name: message.name || 'Unknown',
      ipAddress: clientIp,
      ws: ws,
      lastSeen: Date.now()
    };

    this.clients.set(clientId, clientInfo);
    ws.clientId = clientId;

    // Save to database
    this.db.saveContact(clientInfo);

    // Notify all clients about the new peer
    this.broadcastPeerList();

    // Send confirmation
    ws.send(JSON.stringify({
      type: 'registered',
      clientId: clientId,
      peers: Array.from(this.clients.keys()).filter(id => id !== clientId)
    }));

    this.logger.info(`Client registered`, {
      clientId,
      name: clientInfo.name,
      ip: clientIp
    });
  }

  handleHeartbeat(ws, message) {
    const client = this.clients.get(ws.clientId);
    if (client) {
      client.lastSeen = Date.now();
      ws.send(JSON.stringify({ type: 'heartbeat-ack' }));
    }
  }

  handleClientDisconnect(ws) {
    if (ws.clientId) {
      console.log(`Client disconnected: ${ws.clientId}`);
      this.clients.delete(ws.clientId);
      this.broadcastPeerList();
    }
  }

  broadcastPeerList() {
    const peers = Array.from(this.clients.values()).map(client => ({
      id: client.id,
      name: client.name,
      ipAddress: client.ipAddress,
      lastSeen: client.lastSeen
    }));

    const message = JSON.stringify({
      type: 'peer-list',
      peers: peers
    });

    this.clients.forEach(client => {
      if (client.ws.readyState === WebSocket.OPEN) {
        client.ws.send(message);
      }
    });
  }

  start() {
    this.server.listen(PORT, () => {
      this.logger.info(`LANChat Server started`, {
        httpPort: PORT,
        wsPort: WS_PORT
      });

      // Start network discovery
      this.networkDiscovery.start(PORT, WS_PORT);

      // Setup graceful shutdown
      this.setupGracefulShutdown();

      // Start heartbeat timeout checker
      this.startHeartbeatTimeoutChecker();
    });
  }

  setupGracefulShutdown() {
    const shutdown = () => {
      this.logger.info('Shutting down gracefully...');

      // Stop log rotation
      if (this.logRotationInterval) {
        clearInterval(this.logRotationInterval);
      }

      // Stop heartbeat timeout checker
      if (this.heartbeatTimeoutInterval) {
        clearInterval(this.heartbeatTimeoutInterval);
      }

      // Close database connection
      this.db.close();

      // Stop network discovery
      this.networkDiscovery.stop();

      // Close all WebSocket connections
      this.clients.forEach(client => {
        if (client.ws.readyState === WebSocket.OPEN) {
          client.ws.close();
        }
      });

      // Close servers
      this.wss.close(() => {
        this.logger.info('WebSocket server closed');
      });

      this.server.close(() => {
        this.logger.info('HTTP server closed');
        process.exit(0);
      });
    };

    process.on('SIGINT', shutdown);
    process.on('SIGTERM', shutdown);
  }

  startHeartbeatTimeoutChecker() {
    // Check for timeout clients every 30 seconds
    this.heartbeatTimeoutInterval = setInterval(() => {
      const now = Date.now();
      const TIMEOUT = 60000; // 60 seconds

      this.clients.forEach((client, id) => {
        if (now - client.lastSeen > TIMEOUT) {
          this.logger.info(`Client ${id} timeout, disconnecting`);
          if (client.ws.readyState === WebSocket.OPEN) {
            client.ws.close();
          }
          this.clients.delete(id);
        }
      });
    }, 30000);
  }
}

// Start server
const server = new LANChatServer();
server.start();

module.exports = LANChatServer;
